#ifndef __PROPERTIES_READER_H__
#define __PROPERTIES_READER_H__ 1

#ifdef __cplusplus
#include <map>
#include <ostream>
#include <string>

typedef std::map<std::string, std::string> TSection;
typedef std::map<std::string, TSection*> TSections;
typedef std::map<std::string, std::map<std::string, std::string>*>::const_iterator TSectionIterator;
typedef std::map<std::string, std::string>::const_iterator TKeyIterator;

typedef enum 
{
	OPEN_UNKNOWN = 0x00,
	OPEN_NOAUTOLOAD = 0x01,
	OPEN_AUTOLOAD = 0x02,
} EOpenFlags;

/**
 * @brief Reads configuration values from a given file.
 * @author mnutzinger
 *
 * Provides an interface to read and interpret the configuration file, which is
 * available in the ini format
 */
class CPropertiesReader
{
public:
	CPropertiesReader(const std::string& pFilename, EOpenFlags pFlags = OPEN_AUTOLOAD);
	virtual ~CPropertiesReader();

	/**
	 * Create a new section. A check is performed to see if it 
	 * already exists.
	 *
	 * @param pSection name of the section to create
	 * @return false if the given sections already exists, false otherwise
	 */
	bool createSection(const std::string& pSection);

	/**
	 * Check whether a given section exists in the read config file.
	 *
	 * @param pSection name of the section to lookup
	 * @return true if the given sections exists, false otherwise
	 */
	bool containsSection(const std::string& pSection);

	/**
	 * Check whether a given key exists in the read config file inside a given
	 * section.
	 *
	 * @param pSection name of the section the key has to searched in
	 * @param pKey name of the key for lookup
	 * @return true if the given key exists, false otherwise
	 */
	bool containsKey(const std::string& pSection, const std::string& pKey);

	/**
	 * Retrieve value to given key from a given section of the config file.
	 *
	 * @param pSection name of the section
	 * @param pKey name of the key
	 * @param pValue reference parameter to which the value for pKey is copied
	 * @return true if the given key exists, false otherwise
	 */
	bool getValue(const std::string& pSection, const std::string& pKey,
			std::string& pValue);

	/**
	 * Set a value for a given (existing) key in a given section.
	 *
	 * @note the new value is only set in the in-memory configuration. the
	 * actual ini file is not touched.
	 *
	 * @param pSection name of the section
	 * @param pKey name of the key
	 * @param pValue new value for pKey
	 * @return error status
	 */
	bool setValue(const std::string& pSection, const std::string& pKey,
			const std::string& pValue);
	
	/**
	 * Create a value for a given key in a given section.
	 *
	 * @note the new value is only set in the in-memory configuration. the
	 * actual ini file is not touched.
	 *
	 * @param pSection name of the section
	 * @param pKey name of the key
	 * @param pValue new value for pKey
	 * @return error status
	 */
	bool createKey(const std::string& pSection,
			const std::string& pKey, const std::string& pValue);

	/**
	 * Change the filename to a new one
	 *
	 * @param pFilename the path to the new file
	 */
	void changeFile(const std::string& pFilename);

	/**
	 * Dump the read configuration (ini) file to the given stream
	 *
	 * @param pOut stream to which the configuration values should be written
	 */
	void dump(std::ostream& pOut);

	/**
	 * Write the read configuration (ini) file to the associated file
	 * 
	 * @return error status
	 */
	bool write();

	/**
	 * Load the ini file section, keys and values.
	 * This methode will be called automatically by containsSection,
	 * containsKey, etc. if it is necessary. This methode can be also use
	 * to check, if the config file is found and has no errors.
	 *
	 * @return error status
	 */
	bool loadIniFile();

	bool getSectionIteratorBegin(TSectionIterator& pIterator);
	bool getSectionIteratorEnd(TSectionIterator& pIterator);
	bool getKeyIteratorBegin(std::string pSection, TKeyIterator& pIterator);
	bool getKeyIteratorEnd(std::string pSection, TKeyIterator& pIterator);

	/**
	 * Clear all maps which store the configuration values
	 */
	void dropConfig();

	inline void setLoaded(bool pLoaded) { mLoaded = pLoaded; }
	inline bool getLoaded() const { return mLoaded; }


private:
	CPropertiesReader(const CPropertiesReader& );
	CPropertiesReader& operator=(const CPropertiesReader& );

protected:
	CPropertiesReader();

	/**
	 * path of the config file
	 */
	std::string mFilename;

	/**
	 * map which corresponds section names with pointers to maps containing the
	 * keys and values of the specific section
	 */
	TSections mSections;

	/**
	 * true if the map has been initialized with the file contents, false
	 * otherwise
	 */
	bool mLoaded;

	/**
	 * contains flags set by the constructor for this properties reader
	 */
	EOpenFlags mFlags;

	// ****************
	// helper functions
	// ****************

	/**
	 * get the map which holds the key, value pairs for a specific section
	 *
	 * @param pSection the desired section
	 * @param pMap a pointer to the map holding the section configuration
	 * @return true if the section was found, false otherwise
	 */
	bool getMapForSection(const std::string& pSection,
			TSection*& pMap);

	/**
	 * get the value for a given key from a given section
	 *
	 * @param pSection the desired section
	 * @param pKey the desired key in pSection
	 * @param pValue reference parameter to which the value of the key is copied
	 * @return true if a value for the key exists, false otherwise
	 */
	bool getValueForKey(const std::string& pSection, const std::string& pKey,
			std::string& pValue);
};
#else
typedef struct CPropertiesReader CPropertiesReader;
#endif /* __cplusplus */

#endif // __PROPERTIES_READER_H__

